這篇介紹一下 JS 中內建的日期物件 Date
。
JS 中的 Date
採用的紀錄方式是 Unix 時間戳。
Unix 時間戳 是時間眾多紀錄方式中的其中一種,這種方式會以西元 1970/01/01 00:00:00 UTC 為原始點來紀錄時間。
以 JS 的實現而言,雖然命名叫 Date
,時間紀錄的最小單位為 毫秒 而非僅到日期。(為求表述方便,後面如果我說日期物件就是指 Date
)
let timestamp1 = 0;
let date1 = new Date(timestamp1);
console.log(date1.toUTCString());//"Thu, 01 Jan 1970 00:00:00 GMT"
let timestamp2 = 1;
let date2 = new Date(timestamp2);
console.log(date2.toUTCString());//"Thu, 01 Jan 1970 00:00:00 GMT"
//看不出差別是因為只差一毫秒,上面的表示方式看不到毫秒
console.log(date2 - date1);//1 相減可以得到時間戳差
透過這個系統,實際上 Date
物件內部是有個屬性以數字在儲存時間戳。
雖然這個內部屬性無法直接存取,但我們可以使用 Date.getTime()
方式來得到物件上的時間戳。
let now = new Date("2024-10-09");
console.log(now.getTime());//1728432000000
let nowByTimeStamp = new Date(1728432000000);
console.log(nowByTimeStamp);
//Wed Oct 09 2024 08:00:00 GMT+0800 (Taipei Standard Time)
同時,JS 的 Date
物件也支援負數時間戳,即以負數表示 1970/01/01 以前的時間。
let before1970 = -11131561920000;
let before1970Date = new Date(before1970);
console.log(before1970Date.toUTCString());
//"Mon, 03 Apr 1617 11:28:00 GMT"
至於建構 Date
物件的方法,就以 new Date()
方式來建構,允許傳入的參數非常多,詳情請見MDN。
Date
物件只能使用 new Date()
建構方式來建立,如果是呼叫 Date()
,會是嘗試將其轉為字串回傳,而非回傳 Date
物件。
console.log(typeof new Date("2024-10-09"));//"object"
console.log(typeof Date("2024-10-09"));//"string"
透過 Unix 時間戳紀錄時間的好處有以下三種:
> <
都能使用,但比較相等的話要用 getTime()
後再比較,否則會變為物件的比較而通常不如預期。Date
物件並不提供透過 []
的方式來訪問屬性,所有的訪問都基於提供的方法。
方法過多我就不一列出,請直接參考上面附上的 MDN 文件左側。
基本就是 get
或 set
後面加上時間單位,命名都蠻直覺的。
每個國家甚至地區往往都有各自不同習慣的時間計數方式,這件事在國際交流時是相當麻煩的。
後來由 國際標準化組織(ISO) 推出了 ISO8601 來做為一個通用的時間表述標準。
日期表述為 YYYY-MM-DD
時間表述為 hh:mm:ss
時間加日期表述為 YYYY-MM-DD hh:mm:ss
對時區的表述 透過 ±hh:mm 來表述,比如在後方加上 +08:00 表示 UTC+8 時區的時間
僅表示標準的 UTC+0 可以簡寫做 Z。
特別提到這個標準是因為這個標準應用廣泛,開發者們應該多以習以為常,甚至可能沒意識到這是一個標準。
只要有提到 ISOString,指的就是符合這個 ISO8601 標準的時間表述字串。
字串永遠由 24 或 27 個字元組成,有兩種表述方式:
YYYY-MM-DDTHH:mm:ss.sssZ
或 ±YYYYYY-MM-DDTHH:mm:ss.sssZ
。
JS 的日期有一些比較要注意的地方,如 monthIndex
的 1 月的值是 0(注意,不是 month
,特指 monthIndex
,用於建構 Date
物件)。
另外日期裡的各個位數是會有溢出時進位和不足時借位的狀況的(因為背後是時間戳表述,各個單位其實就是乘以對應的基數最後計算為一個時間戳整數,自然會有進位和借位的狀況),如 monthIndex
寫了 12,則年會 +1,而月變為一月。要注意各個單位是否有在各自的範圍內。
JS 的時間戳允許上下限為 -8,640,000,000,000,000 到 8,640,000,000,000,000。
若超出這個數字,內部計數會變為 NaN
,而嘗試以字串顯示日期時則會得到 Invalid Date
。
let maxDate = new Date(8640000000000000);
let overMaxDate = new Date(8640000000000001);
console.log(maxDate.toISOString());//"+275760-09-13T00:00:00.000Z"
console.log(overMaxDate.getTime());//NaN
console.log(overMaxDate.toISOString());//Uncaught RangeError: Invalid time value"
儘管 JS 的日期物件能夠轉換為特定時區方式來顯示,但儲存時皆基於 UTC,也沒有特別好的轉換方式,比如常見時區的 PT,CT 等等,需要自己手動撰寫。
網路上有許多他人寫好的專門處理時間的函式庫,如果專案會頻繁使用到跨時區的時間,且希望儲存時間帶有時區表述能力,建議使用函式庫輔助,會在操作上更為便利。(如常見的 moment.js)
時間的處理上有很多細部的細節要注意,例如日光節約時間(夏令時間)、閏秒(JS 的 Date
物件並不表述閏秒)等等,要考慮自己的系統需要到多深的實作,否則連時間欄位都亂掉,系統肯定會一團混亂。
至此關於 JS 的日期物件大致介紹告一段落,記得並理解時間戳的概念,會對操作時間有很大的幫助。